package com.example.ws.handler.elsearch;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpHost;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.example.ws.GlobalConfLoader;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ElSearchClient {

    private RestHighLevelClient client;

    public ElSearchClient() {
        this.client = createElasticsearchClient(
                GlobalConfLoader.getConfprop().getProperty(GlobalConfLoader.CONFIG_KEY_ELASTICSEARCH_IP),
                Integer.parseInt(
                        GlobalConfLoader.getConfprop().getProperty(GlobalConfLoader.CONFIG_KEY_ELASTICSEARCH_PORT)));
    }

    // public ElSearchClient(String host, int port) {
    // this.client = createElasticsearchClient(host, port);
    // }

    // Method to create Elasticsearch client
    private RestHighLevelClient createElasticsearchClient(String host, int port) {
        try {
            return new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost(host, port, "http")));
        } catch (Exception e) {
            log.error("Error creating Elasticsearch client", e);
            throw new RuntimeException("Failed to create Elasticsearch client", e);
        }
    }

    // Method to close the client connection
    public void closeClient() {
        try {
            if (client != null) {
                client.close();
                log.info("Elasticsearch client closed successfully");
            }
        } catch (Exception e) {
            log.error("Error closing Elasticsearch client", e);
        }
    }

    // Getter for the client (useful for dependency injection or testing)
    public RestHighLevelClient getClient() {
        return this.client;
    }

    /**
     * 新增文档到指定索引
     * 
     * @param indexName 索引名称
     * @param document  要存储的文档数据
     * @return 索引响应
     * @throws IOException 处理异常
     */
    public IndexResponse indexDocument(String indexName, Map<String, Object> document) throws IOException {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonDocument = objectMapper.writeValueAsString(document);
            IndexRequest indexRequest = new IndexRequest(indexName)
                    .source(jsonDocument, XContentType.JSON);

            IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
            log.info("Document indexed successfully: {}", response.getId());
            return response;
        } catch (IOException e) {
            log.error("Error indexing document", e);
            throw e;
        }
    }

    /**
     * 更新指定索引中的文档
     * 
     * @param indexName     索引名称
     * @param documentId    文档ID
     * @param updatedFields 要更新的字段
     * @return 更新响应
     * @throws IOException 处理异常
     */
    public UpdateResponse updateDocument(String indexName, String documentId, Map<String, Object> updatedFields)
            throws IOException {
        try {
            UpdateRequest updateRequest = new UpdateRequest(indexName, documentId)
                    .doc(updatedFields);

            UpdateResponse response = client.update(updateRequest, RequestOptions.DEFAULT);
            log.info("Document updated successfully: {}", response.getId());
            return response;
        } catch (IOException e) {
            log.error("Error updating document", e);
            throw e;
        }
    }

    /**
     * 删除指定索引中的文档
     * 
     * @param indexName  索引名称
     * @param documentId 文档ID
     * @return 删除响应
     * @throws IOException 处理异常
     */
    public DeleteResponse deleteDocument(String indexName, String documentId) throws IOException {
        try {
            DeleteRequest deleteRequest = new DeleteRequest(indexName, documentId);
            DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
            log.info("Document deleted successfully: {}", response.getId());
            return response;
        } catch (IOException e) {
            log.error("Error deleting document", e);
            throw e;
        }
    }

    /**
     * 精确查询：根据特定字段精确匹配
     * 
     * @param indexName 索引名称
     * @param field     查询字段
     * @param value     查询值
     * @return 查询结果列表
     * @throws IOException 查询异常
     */
    public List<Map<String, Object>> searchByTermQuery(String indexName, String field, Object value)
            throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 精确查询
        sourceBuilder.query(QueryBuilders.termQuery(field, value));
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        return processSearchResponse(searchResponse);
    }

    /**
     * 模糊查询：支持部分匹配
     * 
     * @param indexName 索引名称
     * @param field     查询字段
     * @param value     查询值
     * @return 查询结果列表
     * @throws IOException 查询异常
     */
    public List<Map<String, Object>> searchByMatchQuery(String indexName, String field, String value)
            throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 模糊查询
        sourceBuilder.query(QueryBuilders.matchQuery(field, value));
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        return processSearchResponse(searchResponse);
    }

    /**
     * 复合查询：支持多条件组合查询
     * 
     * @param indexName        索引名称
     * @param mustConditions   必须匹配的条件
     * @param shouldConditions 可选匹配的条件
     * @return 查询结果列表
     * @throws IOException 查询异常
     */
    public List<Map<String, Object>> searchByBoolQuery(
            String indexName,
            Map<String, Object> mustConditions,
            Map<String, Object> shouldConditions) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 创建布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 添加必须匹配的条件
        if (mustConditions != null) {
            mustConditions.forEach((field, value) -> boolQueryBuilder.must(QueryBuilders.termQuery(field, value)));
        }

        // 添加可选匹配的条件
        if (shouldConditions != null) {
            shouldConditions.forEach((field, value) -> boolQueryBuilder.should(QueryBuilders.termQuery(field, value)));
        }

        sourceBuilder.query(boolQueryBuilder);
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        return processSearchResponse(searchResponse);
    }

    /**
     * 分页查询
     * 
     * @param indexName 索引名称
     * @param pageNum   页码
     * @param pageSize  每页大小
     * @return 查询结果列表
     * @throws IOException 查询异常
     */
    public List<Map<String, Object>> searchWithPagination(
            String indexName,
            int pageNum,
            int pageSize) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 设置分页
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);

        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        return processSearchResponse(searchResponse);
    }

    /**
     * 处理搜索响应，转换为易用的列表
     * 
     * @param searchResponse Elasticsearch搜索响应
     * @return 查询结果列表
     */
    private List<Map<String, Object>> processSearchResponse(SearchResponse searchResponse) {
        List<Map<String, Object>> results = new ArrayList<>();

        for (SearchHit hit : searchResponse.getHits().getHits()) {
            results.add(hit.getSourceAsMap());
        }

        return results;
    }

    /**
     * 获取指定索引的所有文档
     * 
     * @param indexName 索引名称
     * @return 所有文档列表
     * @throws IOException 查询异常
     */
    public List<Map<String, Object>> searchAllDocuments(String indexName) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 查询所有文档
        sourceBuilder.query(QueryBuilders.matchAllQuery());

        // 设置最大返回文档数，防止内存溢出
        sourceBuilder.size(10000); // 可根据实际情况调整

        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        return processSearchResponse(searchResponse);
    }

    /**
     * 获取指定索引的所有文档（支持分页）
     * 
     * @param indexName 索引名称
     * @param pageNum   页码
     * @param pageSize  每页大小
     * @return 分页文档列表
     * @throws IOException 查询异常
     */
    public List<Map<String, Object>> searchAllDocumentsWithPagination(
            String indexName,
            int pageNum,
            int pageSize) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 查询所有文档
        sourceBuilder.query(QueryBuilders.matchAllQuery());

        // 设置分页
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);

        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        return processSearchResponse(searchResponse);
    }

    /**
     * 获取所有索引的所有文档
     * 
     * @return 包含所有索引文档的映射，键为索引名，值为该索引的所有文档
     * @throws IOException 查询异常
     */
    public Map<String, List<Map<String, Object>>> searchAllIndicesDocuments() throws IOException {
        // 获取所有索引名称
        GetIndexRequest request = new GetIndexRequest("*");
        IndicesClient indicesClient = client.indices();
        GetIndexResponse getIndexResponse = indicesClient.get(request, RequestOptions.DEFAULT);
        String[] indexNames = getIndexResponse.getIndices();
    
        // 存储结果的映射
        Map<String, List<Map<String, Object>>> allDocuments = new HashMap<>();
    
        // 遍历每个索引并获取其所有文档
        for (String indexName : indexNames) {
            List<Map<String, Object>> indexDocuments = searchAllDocuments(indexName);
            if (!indexDocuments.isEmpty()) {
                allDocuments.put(indexName, indexDocuments);
            }
        }
    
        return allDocuments;
    }
}
